package com.gusto.engine.clusterant.algorithms.antsclustering.ants;

import java.util.List;

import org.apache.log4j.Logger;

import com.gusto.engine.clusterant.algorithms.antsclustering.Neighbor;
import com.gusto.engine.clusterant.algorithms.antsclustering.Plan;

/**
 * <p>An ant that moves randomly.</p>
 * 
 * @author amokrane.belloui@gmail.com
 * 
 */
public class AntRandom extends Ant {
	
	private Logger log = Logger.getLogger(getClass());
	
	public static Integer zProbPickCount = 0;
	public static Double zProbPickSum = 0D;
	public static Integer zProbDropCount = 0;
	public static Double zProbDropSum = 0D;
	
	
	protected Double thresholdPick; // INIT
	protected Double thresholdDrop; // INIT
	
	protected int bestRow;
	protected int bestCol;
	protected double bestSim;
	
	
	public AntRandom(Plan plan, Long maxMoves, Integer maxMovesWithObject, Double thresholdPick, Double thresholdDrop) {
		super(plan, maxMoves, maxMovesWithObject);
		this.thresholdPick = thresholdPick;
		this.thresholdDrop = thresholdDrop;
	}
	
	/**
	 * Move randomly
	 */
	protected void move() {
		row = Integer.parseInt(Math.round(Math.random() * (plan.getRows()-1)) + "");
		col = Integer.parseInt(Math.round(Math.random() * (plan.getCols()-1)) + "");
	}
	
	/**
	 * {@inheritDoc}
	 */
	protected synchronized void doPick() {
		Object objectOnPlan = plan.getObject(this.row, this.col);
		
		List<Neighbor> neighbors = plan.getNeighbors(this.row, this.col, this.perception); 
		Double similarity = plan.getSimilarityNeighbors(objectOnPlan, neighbors);
		
		Double probability = 1 - similarity;
		zProbPickCount ++;
		zProbPickSum += probability;
		if (probability >= thresholdPick) {
			pick();
			bestRow = this.row;
			bestCol = this.col;
			bestSim = similarity;
			//log.debug("pick " + object + " " + row + "-" + col);
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	protected synchronized void doDrop() {
		List<Neighbor> neighbors = plan.getNeighbors(this.row, this.col, this.perception);
		Double similarity = plan.getSimilarityNeighbors(object, neighbors);
		
		Double probability = similarity;
		zProbDropCount ++;
		zProbDropSum += probability;
		if (probability >= thresholdDrop) {
			gotoBestPosition();
			//log.debug("drop " + object + " " + row + "-" + col);
			drop();
		}
	}
	
	/**
	 * Before dropping go to the best position found along the way
	 */
	private void gotoBestPosition() {
		Double s = plan.getSimilarityNeighbors(object, plan.getNeighbors(row, col, this.perception));
		if (s > bestSim) {
			bestRow = this.row;
			bestCol = this.col;
			bestSim = s;
		}
		
		for (int i = row - perception; i <= row + perception; i++) {
			for (int j = col - perception; j <= col + perception; j++) {
				if (i>=0 && i<plan.getRows() && j>=0 && j<plan.getCols()) {
					
					if (!(row == i && col == j)) {
						if (plan.isEmpty(i, j)) {
							Double sim = plan.getSimilarityNeighbors(object, plan.getNeighbors(i, j, this.perception));
							if (sim > bestSim) {
								bestSim = sim;
								bestRow = i;
								bestCol = j;
							}
						}
					}
					
				}
			}
		}
		
		if (row != bestRow && col != bestCol) {
			//log.debug("Go to best position : " + row + "-" + col + " => " + bestRow + "-" + bestCol);
			//this.moveTo(bestRow, bestCol);
			this.row = bestRow;
			this.col = bestCol;
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	protected void doForcedDrop(boolean endJob) {
		Double probability = bestSim;
		if (probability > thresholdDrop) {
			//moveTo(bestRow, bestCol);
			this.row = bestRow;
			this.col = bestCol;
		} 
	}
	
	/**
	 * {@inheritDoc}
	 */
	protected void onFinishedJob() {
		log.info("Pick Probability " + (zProbPickSum / zProbPickCount));
		log.info("Drop Probability " + (zProbDropSum / zProbDropCount));
	}
}
